# 现有一数据集logist.Txt，要求给定的数据来完成如下要求：
# ①	要求导入相关包并加载logistics.txt数据。（5）
import numpy as np
import matplotlib.pyplot as plt

data = np.loadtxt('logist.txt', delimiter=',')

# ②	要求将数据正确切分。（5）
x = data[:, :-1]
y = data[:, -1]
m = len(x)

# ③	要求特征缩放（标准化特征缩放，其它特征缩放得分一半）（5）
mu = x.mean(axis=0)
sigma = x.std(axis=0)
x -= mu
x /= sigma

# ④	进行数据拼接，并且将数据按4：1比例分成训练集和测试集（5）
XX = np.c_[np.ones(m), x]
np.random.seed(1)
a = np.random.permutation(m)
x = x[a]
XX = XX[a]
y = y[a]
m_train = int(0.8 * m)
m_test = m - m_train
XX_train, XX_test = np.split(XX, [m_train])
y_train, y_test = np.split(y, [m_train])


# ⑤	正确写出预测模型函数（5）
def model(XX, theta):
    return XX.dot(theta)


def sigmoid(z):
    return 1 / (1 + np.exp(-z))


# ⑥	正确写出代价（损失）函数（5）
def cost_func(h, y):
    m = len(h)
    return -1 / m * np.sum(y * np.log(h) + (1 - y) * np.log(1 - h))


# ⑦	正确梯度函数，要求学习率0.1，迭代步长10000（5）
def grad(XX, y, alpha=0.1, iter0=10000):
    m, n = XX.shape
    group = iter0 // 20
    theta = np.zeros(n)
    j_his = np.zeros(iter0)
    for i in range(iter0):
        h = sigmoid(model(XX, theta))
        j = cost_func(h, y)
        j_his[i] = j
        if i % group == 0:
            print(f'#{i + 1} cost func value = {j}')
        dt = 1 / m * XX.T.dot(h - y)
        theta -= alpha * dt
    if i % group != 0:
        print(f'#{i + 1} cost func value = {j}')
    return theta, j_his, h


# ⑧	正确写出正确率（accurancy）函数（5）
def accuracy(h, y):
    return np.mean(y == (h > 0.5))


# ⑨	要求输出权重值，和准确率值（5）
theta, j_his, h_train = grad(XX_train, y_train)
print(f'Weight = {theta}')
print(f'Accuracy (training) = {accuracy(h_train, y_train)}')
h_test = sigmoid(model(XX_test, theta))
print(f'Accuracy (testing) = {accuracy(h_test, y_test)}')

# ⑩	数据可视化：
# （1）画出代价函数图（2）
plt.figure(figsize=[16, 8])
spr = 1
spc = 2
spn = 1
plt.subplot(spr, spc, spn)
plt.plot(j_his, label='cost function')
plt.xlabel('Iteration')
plt.grid()
plt.legend()

# （2）画出样本点，并画出边界线。（3）
spn += 1
plt.subplot(spr, spc, spn)
pos_idx = y == 1
neg_idx = np.invert(pos_idx)
plt_x = np.array([x[:, 0].min(), x[:, 0].max()])
plt_y = - (theta[0] + theta[1] * plt_x) / theta[2]
plt.scatter(x[pos_idx, 0], x[pos_idx, 1], s=1, c='r', label='positive')
plt.scatter(x[neg_idx, 0], x[neg_idx, 1], s=1, c='b', label='negativee')
plt.plot(plt_x, plt_y, 'g--', label='border')
plt.grid()
plt.legend()

plt.show()
